#include <stdio.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <time.h>
#include <algorithm>

#include "strategies.h"

DWORD MAX_FILE_SIZE = 1024*1024*1024*2;

bool isNumber(const char& a)
{
	if(a <= '9' && a >= '0')
		return true;
	return false;
}

int charToInt(const char& a)
{
	if(isNumber(a))
		return a - 48;
	return -1;
}

std::string intToString(const int& i)
{
	std::ostringstream out;
	out << i;
	std::string temp_str = out.str() + " ";
	return temp_str;
}

int power(const int& number, const int& power)
{
	int result = 1;
	if(power == 0)
		return result;
	for(int i = 1; i <= power; i++)
		result *= 10;
	return result;
}

int stringToInt(const std::string str)
{
	int result = 0;
	for(int i = 0; i < str.length(); i++)
		result = result * 10 + charToInt(str[i]);
	return result;
}

//sort function
void insertionArraySort(int* array, long size) {
	std::sort(array, array + size);
}

//function got from net. just for transforming
std::wstring s2ws(const std::string& s)
{
	int len;
	int slength = (int)s.length() + 1;
	len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
	wchar_t* buf = new wchar_t[len];
	MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
	std::wstring r(buf);
	delete[] buf;
	return r;
}

bool createVectorFromChar(const char* input, std::vector<int>& vector_to_sort)
{
	std::cout << "Start creating vector\n";
	long current_number = 0;
	long vectorIncrement = 0;
	long space_count = 0;
	for(int i = 0; input[i] != NULL; i++)
	{
		if(input[i] == ' ')
			space_count++;
	}
	vector_to_sort.resize(space_count, 0);
	for(int i = 0; input[i] != NULL; i++)
	{
		if(isNumber(input[i]))
		{
			while(isNumber(input[i]) && input[i] != NULL)
			{
				current_number = current_number * 10 + charToInt(input[i]);
				i++;
			}
			i--;
			vector_to_sort[vectorIncrement] = current_number;
			vectorIncrement++;
			current_number = 0;
		}
		else if(input[i] != ' ' && input[i] != '\n')
			return false;
	}
	std::cout << "End creating vector\n";
	return true;
}

bool createArrayFromChar(const char* input, int* vector_to_sort)
{
	std::cout << "Start creating vector\n";
	long current_number = 0;
	long vectorIncrement = 0;
	for(int i = 0; input[i] != NULL; i++)
	{
		if(isNumber(input[i]))
		{
			while(isNumber(input[i]) && input[i] != NULL)
			{
				current_number = current_number * 10 + charToInt(input[i]);
				i++;
			}
			i--;
			vector_to_sort[vectorIncrement] = current_number;
			vectorIncrement++;
			current_number = 0;
		}
		else if(input[i] != ' ' && input[i] != '\n')
			return false;
	}
	std::cout << "End creating vector\n";
	return true;
}

void HandleLastError(const char *msg) {
	DWORD errCode = GetLastError();
	LPVOID err;
	if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		errCode,
		NULL, // default language
		(LPTSTR)&err,
		0,
		NULL))
		return;
	std::wcout << msg << err << "\n";
	LocalFree(err);
}

int main(int argc, char* argv[])
{
	std::vector<long> duration_time;
	long start = time(0);
	SYSTEMTIME systemTime;
	GetSystemTime( &systemTime );
	start = (systemTime.wSecond * 1000) + systemTime.wMilliseconds;
	long average = 0;
	if(argc != 3)
	{
		if(argc < 3)
			std::cout << "Not enough arguments" << "\n";
		else if(argc > 3)
			std::cout << "Too many arguments" << "\n";
		std::cout << "Program need 2 arguments: input data file and output data file where the program will put sorted array\n";
		return 0;
	}

	//<--------Reding from file

	std::string str(argv[1]);

	//creating file
	HANDLE inputFile = CreateFile(s2ws(str).c_str(),GENERIC_READ|GENERIC_WRITE,0,NULL, OPEN_EXISTING,0,NULL);

	//getting size of the file
	DWORD dwFileSizeHi = 0;
	DWORD fileSize = GetFileSize(inputFile, &dwFileSizeHi);

	if(GetLastError() != 0)
	{
		if(GetLastError() == ERROR_INVALID_HANDLE)
			//HandleLastError("File doesn't exist: "); <----tried to do it like this, but output was not good.
			//I believe problems with coding, but i'm not sure, because i've tried a lot of parameters
			std::cout << "File doesn't exist: "<< argv[1] <<"\n";
		else std::cout << "Error with openning file: " << argv[1] << "\n";
		return 0;
	}
	if(fileSize > MAX_FILE_SIZE || fileSize < 0)
	{
		std::cout << "File is too big: " << argv[1] << "\n";
		return 0;
	}

	//mapping file
	HANDLE hMapping = CreateFileMapping(inputFile, NULL, PAGE_READONLY, 0, 0, NULL);
	if(hMapping == NULL)
	{
		if(GetLastError() == ERROR_INVALID_PARAMETER)
			std::cout << "File doesn't exist: "<< argv[1] <<"\n";
		else if(GetLastError() == ERROR_ACCESS_DENIED)
			std::cout << "Access denied to file: " << argv[1] << "\n";
		else std::cout << "Can't specify error with code: " << GetLastError() << "\n";
		return 0;
	}
	//input text
	char* char_input =(char*)MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0);

	//creating array
	long space_count = 0;
	for(int i = 0; char_input[i] != NULL; i++)
	{
		if(char_input[i] == ' ')
			space_count++;
	}
	int * numbers = new int[space_count];

	if(!createArrayFromChar(char_input, numbers))
	{
		std::cout << "Wrong input format" << "\n";
		return 0;
	}
	else
	{
		GetSystemTime( &systemTime );
		long beforeSort = (systemTime.wSecond * 1000) + systemTime.wMilliseconds;
		std::cout << "Time before sorting \n" << beforeSort;
		//insertionArraySort(numbers, space_count);
		SortStrategies sorts;
		sorts.setStrategy(space_count);
		sorts.sort(numbers);
		GetSystemTime( &systemTime );
		long afterSort = (systemTime.wSecond * 1000) + systemTime.wMilliseconds;
		std::cout << "Time before sorting \n" << afterSort;
		std::cout << "Sorting took: " << afterSort - beforeSort << "\n";
	}



	UnmapViewOfFile(char_input);
	CloseHandle(hMapping);
	CloseHandle(inputFile);
	//end reading from file--------------->

	//<--------Writing into file
	str = argv[2];
	HANDLE output_file = CreateFile(s2ws(str).c_str(),GENERIC_WRITE|GENERIC_READ,0,NULL, CREATE_ALWAYS, 0,0);
	if(GetLastError() == ERROR_ACCESS_DENIED)
	{
		std::cout << "Can't access folder to create: " << argv[2] << "\n";
		return 0;
	}

	HANDLE output_mapping = CreateFileMapping(output_file, NULL, PAGE_READWRITE, 0, fileSize, 0);
	if(output_mapping == NULL)
	{
		std::cout << "CreateFileMapping returned null" << "\n Error: " << GetLastError() << "\n";
		return 0;
	}

	char* char_output = (char*)MapViewOfFile(output_mapping,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
	if(char_output == NULL)
	{
		std::cout <<"Unknown error with code: " << GetLastError() << "\n";
		return 0;
	}

	//writing vector into file
	int caret = 0;
	char buffer[40];
	for(int i = 0; i < space_count; i++){
		itoa(numbers[i], buffer, 10);
		int bufSize = 0;
		while(buffer[bufSize] != '\0')
		{
			bufSize++;
		}
		buffer[bufSize] = ' ';
		bufSize++;
		memcpy(char_output + caret, buffer, bufSize);
		caret += bufSize;
	}

	UnmapViewOfFile(char_output);
	CloseHandle(output_mapping);
	CloseHandle(output_file);
	long end = time(0);
	GetSystemTime( &systemTime);
	end = (systemTime.wSecond * 1000) + systemTime.wMilliseconds;
	duration_time.push_back(end - start);
	delete[] numbers;
	std::cout << "Average time is: " << end-start << "\n";
	//end writing into file------------->
}

